﻿using Cl.Entity.DataBaseEntity.System;
using Cl.Repository.Sqlsugar;
using Cl.Utils.Helper;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Net;

namespace Cl.WebApi.Authorization.Middleware
{
    /// <summary>
    /// 
    /// </summary>
    public class RequestLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="next"></param>
        /// <param name="loggerFactory"></param>
        public RequestLoggingMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
        {
            _next = next;
            _logger = loggerFactory.CreateLogger<RequestLoggingMiddleware>();
        }

        /// <summary>
        /// 异步执行日志记录
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task InvokeAsync(HttpContext context)
        {
            var request = context.Request;
            var response = context.Response;
            var stopwatch = new Stopwatch();
            stopwatch.Start();

            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                //统一处理返回
                LoggerHelper.Error(Utils.Primitives.Enum.LogEnum.System, ex.Message);
                await HandleExceptionAsync(context, ex);
            }
            finally
            {
                stopwatch.Stop();

                // 获取IP地址
                var ipAddress = context.Connection.RemoteIpAddress;
                string userIp = ipAddress.ToString();

                // 获取设备信息可以通过User-Agent HTTP头部来获取
                var userAgent = context.Request.Headers["User-Agent"];
                string userDevice = userAgent.ToString();

                var elapsedMilliseconds = stopwatch.ElapsedMilliseconds;
                var statusCode = response.StatusCode;

                var message = $"Request {request.Method} {request.Path} {request.QueryString} completed in {elapsedMilliseconds}ms with status code {statusCode}";
                _logger.LogInformation(message);

                //写入日志
                SqlsugarStep.InsertLoginLog(new LoginLog { });
            }


            #region planB
            //var stopwatch = new System.Diagnostics.Stopwatch();
            //stopwatch.Start();

            //try
            //{
            //    await _next(context);
            //}
            //finally
            //{
            //    stopwatch.Stop();

            //    var request = context.Request;
            //    var response = context.Response;

            //    var method = request.Method;
            //    var path = request.Path.Value;
            //    var statusCode = response.StatusCode;
            //    var duration = stopwatch.ElapsedMilliseconds;

            //    _logger.LogInformation(
            //        "Method: {Method}, Path: {Path}, StatusCode: {StatusCode}, Duration: {Duration}ms",
            //        method, path, statusCode, duration);
            //}
            #endregion

        }

        private Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            context.Response.ContentType = "application/json";
            var response = context.Response;

            switch (exception)
            {
                case KeyNotFoundException e:
                    response.StatusCode = (int)HttpStatusCode.NotFound;
                    break;
                case UnauthorizedAccessException e:
                    response.StatusCode = (int)HttpStatusCode.Unauthorized;
                    break;
                default:
                    response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    break;
            }

            var result = JsonConvert.SerializeObject(new
            {
                StatusCode = response.StatusCode,
                Message = exception.Message, // In production, you may want to send a generic message
                StackTrace = exception.StackTrace // Consider not exposing this in production
            });

            return context.Response.WriteAsync(result);
        }
    }
}
